# _*_ coding:utf-8 _*_

import re
import datetime
import json
import inspect
import datetime
import numpy as np
from collections import Counter
from math import ceil

'''
Python-基础-时间日期处理小结 http://www.wklken.me/posts/2015/03/03/python-base-datetime.html
'''
def monthOfDate(date_=''):
    return datetime.datetime.now().strftime("%m")

def monthDict(keyName='month'):
    return {keyName, ['01//1月', '02//2月', '03//3月', '04//4月', '05//5月', '06//6月',
                      '07//7月', '08//8月', '09//9月', '10//10月', '11//11月', '12//12月']}

def moredata2Json(_src):
    class NumpyEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj, np.ndarray):
                return obj.tolist()
            return json.JSONEncoder.default(self, obj)

    jsonStr = json.dumps(_src.moredata, indent=4, cls=NumpyEncoder, ensure_ascii=False)
    return jsonStr


def dictdata2Json(_src):
    class NumpyEncoder(json.JSONEncoder):
        def default(self, obj):
            if isinstance(obj, np.ndarray):
                return obj.tolist()
            return json.JSONEncoder.default(self, obj)

    jsonStr = json.dumps(_src.dictdata, indent=4, cls=NumpyEncoder, ensure_ascii=False)
    return jsonStr


def arrSplit(arr_):
    if isinstance(arr_, list):
        if (len(arr_) < 1):
            return []
        if isinstance(arr_[0], (list, tuple)):
            len_ = len(arr_[0])
            retArr = []
            for i in range(len_):
                arr_tmp = [x[i] for x in arr_]
                retArr.append(arr_tmp)
            return retArr
        else:
            return arr_
    return []


'''
延迟计算属性 https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p10_using_lazily_computed_properties.html
你想将一个只读属性定义成一个property，并且只在访问的时候才会计算结果。 但是一旦被访问后，
你希望结果值被缓存起来，不用每次都去计算。
计算出的值被创建后是可以被修改的
'''
class lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            return value


def get_outer_frame_variables():
    """
    Get a dict of local and global variables of the first outer frame from another file.
    local and global variables of the caller.
    """
    cur_filename = inspect.getframeinfo(inspect.currentframe()).filename
    variables = {}
    for outer_frame in inspect.getouterframes(inspect.currentframe()):
        if (outer_frame.filename != cur_filename):
            variables.update(outer_frame.frame.f_globals)
            variables.update(outer_frame.frame.f_locals)
    return variables


def dateInterval(date1, date2):
    '''
    求两个日期间相差的天数
    '''
    # 替换字符串中的非数字字符为'-'，这样分隔数字的可以是任意非数字字符
    d1 = datetime.datetime.strptime(re.sub(r"\D", '-', date1[0:10]), '%Y-%m-%d')
    d2 = datetime.datetime.strptime(re.sub(r"\D", '-', date2[0:10]), '%Y-%m-%d')
    delta = d1 - d2
    return delta.days


def all_unique(lst):
    '''
    检查给定列表是不是存在重复元素，它会使用 set() 函数来移除所有重复元素。
    '''
    return len(lst) == len(set(lst))


def anagram(first, second):
    '''
    检查两个字符串的组成元素是不是一样的。
    '''
    return Counter(first) == Counter(second)


def chunk(lst, size):
    '''
    给定具体的大小，定义一个函数以按照这个大小切割列表。
    '''
    return list(
        map(lambda x: lst[x * size:x * size + size],
            list(range(0, ceil(len(lst) / size)))))


def compact(lst):
    '''
    将布尔型的值去掉，例如（False，None，0，“”），它使用 filter() 函数。
    '''
    return list(filter(bool, lst))


def spread(arg):
    '''
    将列表内的所有元素，包括子列表，都展开成一个列表。
    '''
    if not isinstance(arg, (list, tuple, np.ndarray)):
        return arg

    ret = []
    for i in arg:
        if isinstance(i, (list, tuple)):
            ret.extend(spread(i))
        else:
            ret.append(i)
    return ret


def deep_flatten(lst):
    '''
    通过递归的方式将列表的嵌套展开为单个列表。
    '''
    result = []
    result.extend(
        spread(list(map(lambda x: deep_flatten(x) if type(x) == list else x, lst))))
    return result


def difference(a, b):
    '''
    返回第一个列表的元素，其不在第二个列表内。如果同时要反馈第二个列表独有的元素，还需要加一句 set_b.difference(set_a)。
    '''
    set_a = set(a)
    set_b = set(b)
    comparison = set_a.difference(set_b)
    return list(comparison)


def has_duplicates(lst):
    '''
    检查两个列表是不是有重复项。
    '''
    return len(lst) != len(set(lst))


def most_frequent(list):
    '''
    根据元素频率取列表中最常见的元素。
    '''
    return max(set(list), key = list.count)


def arr2d_slice_act(arr2d, y1, y2, x1, x2, action):
    '''
    从二维数组里切出一个矩形区，把它转为一维数组，并对这个数组进行action计算
    '''
    if (y2 < 0):
        y2 += y1
    elif (y1 == y2):
        y2 = y1 + 1
    if (x2 < 0):
        x2 += x1
    elif (x1 == x2):
        x2 = x1 + 1

    arr2d_ = spread(arr2d[min(y1,y2) : max(y1,y2), min(x1,x2) : max(x1,x2)])
    return action(arr2d_)


if __name__ == '__main__':
    kkk = arr2d_slice_act(arr2d, 8,2, 0,0, np.sum)
    print (kkk)

    print (spread([1,(2,3, [4,5, [6,7]])]))

    print(dateInterval('2019-10-1', '2019-10-10 12:12'))

    global test
    test ='test'
    env = get_outer_frame_variables()
    print (env)

    arr_ = [[1,2], [3,4], [5,6], [7,8], [9,10]]
    print (arrSplit(arr_))

    import math
    class Circle:
        def __init__(self, radius):
            self.radius = radius

        @lazyproperty
        def area(self):
            print('Computing area')
            return math.pi * self.radius ** 2

        @lazyproperty
        def perimeter(self):
            print('Computing perimeter')
            return 2 * math.pi * self.radius

    c = Circle(4.0)
    print (c.radius)
    print (c.area)
    print (c.area)
    print (c.perimeter)
    print (c.perimeter)
